Xenium Data Overview

In this vignette we’ll visualize the spatially-resolved, single cell RNA-seq profiles produced by the Xenium platform. The vignette demonstrates how to load the per-transcript location data, cell x gene matrix, cell segmentation, and cell centroid information available in the Xenium outputs. The resulting Seurat object will contain the gene expression profile of each cell, the centroid and boundary of each cell, and the location of each individual detected transcript. The per-cell gene expression profiles are similar to standard single-cell RNA-seq and can be analyzed using the same tools.

Setup

This vignette requires a work-in-progress version of Seurat. First, you must install in-development versions of Seurat and SeuratObject that have Xenium support. It is recommended to update your package version at this step, as the required versions may not be fully captured in the dependencies yet.

library(remotes)
remotes::install_github("10xGenomics/seurat", "feat/xenium")
library(Seurat)
library(SeuratObject)

Mouse Brain: Load Data

This vignette is based on the mBrain_ff_partial_coronal_section pr-release dataset from 10x Genomics provided in the xenium_prerelease_sept15_mBrain.tar download. These analysis steps are also compatible with the larger mBrain_ff_full_coronal_section, but will take longer to execute. First we read in the dataset and create a Seurat object. Provide the path to the data folder for a Xenium run as the input path. The RNA data is stored in the Xenium assay of the Seurat object. This step should take ~3 minutes for this partial coronal section.

path <- "mBrain_ff_partial_coronal_section/"

# Load the Xenium data
xenium.obj <- LoadXenium(path, fov = "fov")

# remove cells with 0 counts -- these cause problems for SCTransform
xenium.obj <- subset(xenium.obj, subset = nCount_Xenium > 0)

Spatial information is loaded into slots of the Seurat object, labelled by the name of “field of view” (FOV) being loaded. Initially all the data is loaded into the FOV named “fov”, by convention in this vignette. Later, we will make a cropped FOV that zooms into a region of interest. Here is a summary of the spatial information stored in the fov slot of the resulting Seurat object:

Cell Centroids: The spatial coordinates marking the centroid for each cell being profiled
# Get the center position of each centroid. There is one row per cell in this dataframe.
head(GetTissueCoordinates(xenium.obj[["fov"]][["centroids"]]))
x y cell
693.2358 29.92995 1
689.9618 72.50042 2
696.3730 47.63962 3
692.4323 90.86073 4
700.7536 129.89910 5
702.8003 13.28277 6
Cell Segmentation Boundaries: The spatial coordinates that describe the polygon segmentation of each single cell
# Get the coordinates for each segmentation vertice. Each cell will have a variable number of
# vertices describing its shape.
head(GetTissueCoordinates(xenium.obj[["fov"]][["segmentation"]]))
x y cell
689.9322 18.0625 1
686.3198 20.1875 1
686.1072 25.0750 1
684.1948 28.9000 1
684.8323 34.8500 1
684.4073 41.6500 1
Molecule positions: The spatial coordinates for each individual molecule that was detected by the assay
# Fetch molecules positions for Chrm1
head(FetchData(xenium.obj[["fov"]][["molecules"]], vars = "Gad1"))
x y molecule
311.29320 5.351393 Gad1
428.52243 11.754138 Gad1
418.00003 23.427328 Gad1
615.04865 24.853490 Gad1
563.26510 62.926434 Gad1
68.40363 97.489050 Gad1

Standard QC plots

Standard QC plots provided by Seurat are available via the Xenium assay. Here are violin plots of genes per cell (nFeature_Xenium) and transcript counts per cell (nCount_Xenium)

VlnPlot(xenium.obj, features = c("nFeature_Xenium", "nCount_Xenium"), ncol = 2, pt.size = 0, y.max = 800, 
    same.y.lims = F)

Check that the cell and molecule information is loaded correctly by plotting the transcript locations of a few marker gene over the cell centroids. ImageDimPlot() can display cell positions and overlay the location of individual molecules via the molecules argument. The nmols argument is used to downsample the number of transcript locations displayed to control overplotting.

ImageDimPlot(xenium.obj, fov = "fov", molecules = c("Cux2", "Foxp2"), nmols = 20000, mols.cols = c("blue", 
    "green"), axes = T)

Here’s a similar plot, including the pan-inhibitory neuron marker Gad1, inhibitory neuron sub-type markers Pvalb, and Sst, and astrocyte marker Gfap.

ImageDimPlot(xenium.obj, fov = "fov", molecules = c("Gad1", "Sst", "Pvalb", "Gfap"), nmols = 20000)

Here we visualize the expression level of some key layer marker genes at the per-cell level using ImageFeaturePlot() which is analagous to the FeaturePlot() function for visualizing expression on a 2D embedding. We manually adjust the max.cutoff for each gene to roughly the 90th percentile of it’s count distribution to improve contrast.

ImageFeaturePlot(xenium.obj, features = c("Cux2", "Rorb", "Bcl11b", "Foxp2"), max.cutoff = c(25, 
    35, 12, 10), size = 0.75, cols = c("white", "#ff0000"))

Cropped Images and Cell Boundaries

We can zoom in on a chosen area with the Crop() function. Once zoomed-in, we can visualize cell segmentation boundaries along with individual molecules.

cropped.coords <- Crop(xenium.obj[["fov"]], x = c(1400, 2800), y = c(2100, 2900), coords = "plot")

xenium.obj[["zoom"]] <- cropped.coords

# visualize cropped area with cell segmentations & selected molecules
DefaultBoundary(xenium.obj[["zoom"]]) <- "segmentation"
ImageDimPlot(xenium.obj, fov = "zoom", axes = TRUE, border.color = "white", border.size = 0.1, cols = "polychrome", 
    coord.fixed = FALSE, molecules = c("Gad1", "Sst", "Npy2r", "Pvalb", "Nrn1"), nmols = 10000)

Preprocessing and unsupervised analysis

We start by performing a standard unsupervised clustering analysis, essentially first treating the dataset as an scRNA-seq experiment. We use SCTransform-based normalization followed by standard dimensionality reduction and clustering.

xenium.obj <- SCTransform(xenium.obj, assay = "Xenium")
xenium.obj <- RunPCA(xenium.obj, npcs = 30, features = rownames(xenium.obj))
xenium.obj <- RunUMAP(xenium.obj, dims = 1:30)
xenium.obj <- FindNeighbors(xenium.obj, reduction = "pca", dims = 1:30)
xenium.obj <- FindClusters(xenium.obj, resolution = 0.3)

We can then visualize the results of the clustering by coloring each cell according to it’s cluster either in UMAP space (with DimPlot()) or overlaid on the image with ImageDimPlot().

DimPlot(xenium.obj, reduction = "umap")

We can visualize the expression level of the markers we looked at earlier on the UMAP coordinates.

FeaturePlot(xenium.obj, features = c("Cux2", "Bcl11b", "Foxp2", "Gad1", "Sst", "Gfap"))

We can now use ImageDimPlot() to color the cell positions colored by the cluster labels determined in the previous step.

ImageDimPlot(xenium.obj, cols = "polychrome", axes = TRUE, size = 0.75)

This vignette and the analysis methods for in-situ data are a work in progress. Please contact if you have questions or requests for additional examples. If you have any issues with the code working, please report the results of sessionInfo().

Session Info
sessionInfo()
## R version 4.0.3 (2020-10-10)
## Platform: x86_64-conda-linux-gnu (64-bit)
## Running under: Amazon Linux 2
## 
## Matrix products: default
## BLAS/LAPACK: /mnt/opt/R/R-4.0.3-conda/env/lib/libopenblasp-r0.3.12.so
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] sp_1.5-0           SeuratObject_4.1.1 Seurat_4.1.0.9007  remotes_2.2.0     
## 
## loaded via a namespace (and not attached):
##   [1] Rtsne_0.16            colorspace_2.0-3      deldir_1.0-6         
##   [4] ellipsis_0.3.2        ggridges_0.5.3        rstudioapi_0.11      
##   [7] spatstat.data_2.2-0   farver_2.1.1          leiden_0.4.3         
##  [10] listenv_0.8.0         ggrepel_0.9.1         fansi_1.0.3          
##  [13] codetools_0.2-16      splines_4.0.3         knitr_1.30           
##  [16] polyclip_1.10-0       jsonlite_1.8.0        ica_1.0-3            
##  [19] cluster_2.1.0         png_0.1-7             rgeos_0.5-9          
##  [22] uwot_0.1.14           shiny_1.7.2           sctransform_0.3.4    
##  [25] spatstat.sparse_2.1-1 compiler_4.0.3        httr_1.4.4           
##  [28] assertthat_0.2.1      Matrix_1.4-1          fastmap_1.1.0        
##  [31] lazyeval_0.2.2        cli_3.4.0             later_1.3.0          
##  [34] formatR_1.7           htmltools_0.5.3       tools_4.0.3          
##  [37] igraph_1.3.4          gtable_0.3.1          glue_1.6.2           
##  [40] RANN_2.6.1            reshape2_1.4.4        dplyr_1.0.10         
##  [43] Rcpp_1.0.9            scattermore_0.8       vctrs_0.4.1          
##  [46] nlme_3.1-150          progressr_0.11.0      lmtest_0.9-40        
##  [49] spatstat.random_2.2-0 xfun_0.18             stringr_1.4.1        
##  [52] globals_0.16.1        mime_0.12             miniUI_0.1.1.1       
##  [55] lifecycle_1.0.2       irlba_2.3.5           goftest_1.2-3        
##  [58] future_1.28.0         MASS_7.3-53           zoo_1.8-10           
##  [61] scales_1.2.1          spatstat.core_2.4-4   promises_1.2.0.1     
##  [64] spatstat.utils_2.3-1  parallel_4.0.3        RColorBrewer_1.1-3   
##  [67] yaml_2.3.5            curl_4.3.2            reticulate_1.26      
##  [70] pbapply_1.5-0         gridExtra_2.3         ggplot2_3.3.6        
##  [73] rpart_4.1-15          stringi_1.7.8         highr_0.8            
##  [76] rlang_1.0.5           pkgconfig_2.0.3       matrixStats_0.62.0   
##  [79] evaluate_0.14         lattice_0.20-41       tensor_1.5           
##  [82] ROCR_1.0-11           purrr_0.3.4           labeling_0.4.2       
##  [85] patchwork_1.1.2.9000  htmlwidgets_1.5.4     cowplot_1.1.1        
##  [88] tidyselect_1.1.2      parallelly_1.32.1     RcppAnnoy_0.0.19     
##  [91] plyr_1.8.7            magrittr_2.0.3        R6_2.5.1             
##  [94] generics_0.1.3        DBI_1.1.0             mgcv_1.8-33          
##  [97] pillar_1.8.1          fitdistrplus_1.1-8    abind_1.4-5          
## [100] survival_3.2-7        tibble_3.1.8          future.apply_1.9.1   
## [103] crayon_1.5.1          KernSmooth_2.23-17    utf8_1.2.2           
## [106] spatstat.geom_2.4-0   plotly_4.10.0         rmarkdown_2.5        
## [109] grid_4.0.3            data.table_1.14.2     blob_1.2.1           
## [112] digest_0.6.29         xtable_1.8-4          tidyr_1.2.1          
## [115] httpuv_1.6.6          munsell_0.5.0         viridisLite_0.4.1